﻿using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.Linq.Expressions;
using System.Reflection;
using System.Security;
using wwl.Domain.Entities.Common;
using wwl.Domain.Entities.Rbac;
using wwl.SharedContracts.MultiTenancy;

namespace wwl.DataAccess.Context
{
  /// <summary>
  /// 应用程序数据库上下文（EF Core 核心组件）
  /// 负责管理所有领域实体与数据库表的映射关系、数据交互及事务处理
  /// 设计原则：统一数据访问入口，封装领域模型的持久化细节，支撑DDD架构的数据层实现
  /// </summary>
  public class AppDbContext : DbContext
  {
    /// <summary>
    /// 构造函数：通过依赖注入接收数据库上下文配置选项
    /// </summary>
    /// <param name="options">数据库上下文配置（如连接字符串、数据库提供程序、日志配置等），由DI容器自动注入</param>
    /// <remarks>
    /// 1. 配置驱动：通过 options 参数接收外部的数据库配置，实现配置与代码的分离
    /// 2. 依赖注入：构造函数注入确保上下文实例的可测试性和可配置性
    /// 3. 基类传递：将配置选项传递给 EF Core 基类，完成上下文的初始化工作
    /// </remarks>
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
    {
    }

    #region 数据库实体集合（DbSet 属性定义）

    /// <summary>
    /// 租户实体集合（对应数据库中的 rbac_tenant 表）
    /// </summary>
    /// <remarks>
    /// 1. 多租户基础：租户是SaaS系统的核心概念，实现数据的逻辑隔离和管理边界
    /// 2. 业务隔离：每个租户拥有独立的数据空间和配置体系，支持个性化业务需求
    /// 3. 状态管理：支持启用、禁用两种租户状态，控制租户对系统资源的访问权限
    /// 4. 审计完整：完整的创建、更新审计信息，满足企业级应用的合规性要求
    /// </remarks>
    public DbSet<Tenant> Tenants { get; set; } = null!;

    /// <summary>
    /// 用户实体集合（对应数据库中的 rbac_user 表）
    /// </summary>
    /// <remarks>
    /// 1. 核心身份数据：存储系统所有用户的基础身份信息和认证凭据，是权限体系的身份基础
    /// 2. 多租户支持：通过 TenantId 实现用户数据的租户级隔离，支持多租户 SaaS 应用场景
    /// 3. 状态管理：支持启用、禁用、锁定三种用户状态，满足不同业务场景的用户管理需求
    /// 4. 关联完整性：通过导航属性关联用户角色关系，构建完整的用户权限追溯链条
    /// </remarks>
    public DbSet<User> Users { get; set; } = null!;

    /// <summary>
    /// 角色实体集合（对应数据库中的 rbac_role 表）
    /// </summary>
    /// <remarks>
    /// 1. 权限分配载体：角色作为权限的集合单元，实现权限的批量分配和统一管理
    /// 2. 类型分类：系统角色和业务角色的分类管理，满足不同级别的权限管控需求
    /// 3. 租户隔离：各租户可独立定义角色体系，实现完全定制化的权限管理策略
    /// 4. 导航完整：通过导航属性关联角色权限和用户角色，支撑完整的权限查询和分析
    /// </remarks>
    public DbSet<Role> Roles { get; set; } = null!;

    /// <summary>
    /// 用户角色关联实体集合（对应数据库中的 rbac_user_role 表）
    /// </summary>
    /// <remarks>
    /// 1. 权限授予桥梁：建立用户与角色的关联关系，实现"用户通过角色获得权限"的RBAC核心逻辑
    /// 2. 多对多关系：支持用户拥有多个角色、角色被多个用户拥有的灵活权限分配模式
    /// 3. 数据一致性：通过外键约束确保关联的用户和角色存在，维护关联数据的业务有效性
    /// 4. 级联清理：用户或角色删除时自动清理关联记录，确保数据关系的清洁和一致性
    /// </remarks>
    public DbSet<UserRole> UserRoles { get; set; } = null!;

    /// <summary>
    /// 权限实体集合（对应数据库中的 rbac_permission 表）
    /// </summary>
    /// <remarks>
    /// 1. 权限最小单元：定义系统所有可控制的权限项，包括菜单、操作、按钮等不同粒度
    /// 2. 层级结构：支持权限的树形组织结构，实现菜单导航和权限继承的业务需求
    /// 3. 资源映射：通过资源路径实现权限与系统资源的自动匹配，支撑声明式的访问控制
    /// 4. 租户定制：各租户可独立定义权限体系，实现业务功能的完全定制化配置
    /// </remarks>
    public DbSet<Permission> Permissions { get; set; } = null!;

    /// <summary>
    /// 角色权限关联实体集合（对应数据库中的 rbac_role_permission 表）
    /// </summary>
    /// <remarks>
    /// 1. 权限配置核心：建立角色与权限的授权关系，定义每个角色具体拥有的权限范围
    /// 2. 授权管理：通过此集合实现角色的权限授予和回收，支撑灵活的权限配置操作
    /// 3. 权限验证基础：在权限校验时通过角色查找其拥有的所有权限，支撑访问控制决策
    /// 4. 影响分析：权限变更时可快速识别受影响的所有角色和用户，支持变更影响评估
    /// </remarks>
    public DbSet<RolePermission> RolePermissions { get; set; } = null!;

    #endregion

    /// <summary>
    /// 模型创建时的配置（EF Core 初始化阶段调用）
    /// 用于加载所有实体与数据库表的映射配置，构建完整的实体数据模型
    /// </summary>
    /// <param name="modelBuilder">模型构建器，提供流畅API配置实体映射规则和数据库约束</param>
    /// <remarks>
    /// 1. 自动配置：通过反射自动加载程序集中所有实现 IEntityTypeConfiguration 接口的映射配置类
    /// 2. 统一管理：避免手动逐个注册实体配置，简化模型构建的维护工作
    /// 3. 模块化：支持按领域模块组织映射配置，保持代码结构的清晰性和可维护性
    /// 4. 性能优化：EF Core 在首次使用时编译模型，后续使用缓存的编译结果提升性能
    /// </remarks>
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
      // 调用基类方法确保EF Core基础功能的正常初始化
      base.OnModelCreating(modelBuilder);

      // 自动加载当前程序集中所有实现 IEntityTypeConfiguration 接口的映射配置
      // 包括 TenantMap、UserMap、RoleMap、UserRoleMap、PermissionMap、RolePermissionMap 等
      modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);

      // 仅对 ITenantAware 实体应用全局查询过滤器
      foreach (var entityType in modelBuilder.Model.GetEntityTypes())
      {
        // 检查实体是否实现了 ITenantAware 接口
        if (typeof(ITenantAware).IsAssignableFrom(entityType.ClrType))
        {
          // 使用反射来获取 entityType.ClrType 对应的实体类型
          var entityClrType = entityType.ClrType;

          // 定义一个表示 lambda 表达式参数的 Expression.Parameter
          // 参数类型是 entityClrType，参数名称是 "e"
          var parameter = Expression.Parameter(entityClrType, "e");

          // 构建表达式：EF.Property<string?>(e, "TenantId")
          var tenantIdProperty = Expression.Call(
              typeof(EF),
              nameof(EF.Property),
              new[] { typeof(string) }, // 指定属性的类型
              parameter,
              Expression.Constant(nameof(ITenantAware.TenantId)) // 属性名称
          );

          // 构建表达式：TenantContextAccessor.TenantId
          var currentTenantId = Expression.Property(
              null, // 静态属性，所以是 null
              typeof(TenantContextAccessor),
              nameof(TenantContextAccessor.TenantId)
          );

          // 构建表达式：TenantContextAccessor.IsSystemAdmin
          var isSystemAdmin = Expression.Property(
              null, // 静态属性，所以是 null
              typeof(TenantContextAccessor),
              nameof(TenantContextAccessor.IsSystemAdmin)
          );

          // 构建表达式：EF.Property<string?>(e, "TenantId") == TenantContextAccessor.TenantId
          var tenantIdEquals = Expression.Equal(tenantIdProperty, currentTenantId);

          // 构建表达式：(EF.Property<string?>(e, "TenantId") == TenantContextAccessor.TenantId) || TenantContextAccessor.IsSystemAdmin
          var filterExpression = Expression.OrElse(tenantIdEquals, isSystemAdmin);

          // 将完整的表达式树包装成一个 LambdaExpression
          // 它的类型是 Expression<Func<entityClrType, bool>>
          var lambda = Expression.Lambda(filterExpression, parameter);
          //通过泛型方法的正确调用方式
          var entityBuilderMethod = typeof(ModelBuilder).GetMethod(
              nameof(ModelBuilder.Entity),
              new[] { typeof(Type) }
          );
          var entityBuilder = entityBuilderMethod!.Invoke(modelBuilder, new object[] { entityClrType });
          //获取泛型的 HasQueryFilter 方法并调用
          var hasQueryFilterMethod = entityBuilder!.GetType().GetMethod(
              nameof(EntityTypeBuilder<object>.HasQueryFilter),
              new[] { lambda.GetType() }
          );
          hasQueryFilterMethod!.Invoke(entityBuilder, new object[] { lambda });
        }
      }
    }

    /// <summary>
    /// 重写异步保存方法，自动维护所有实体的审计字段（创建时间、更新时间）
    /// 实现对基础实体（BaseEntity）的通用字段自动填充，避免重复代码和遗漏风险
    /// 新增：多租户安全校验（自动设置TenantId + 验证UPDATE/DELETE权限）
    /// </summary>
    /// <param name="cancellationToken">取消令牌，用于在长时间运行的操作中支持协作式取消</param>
    /// <returns>受影响的数据记录行数，用于判断操作执行结果和后续处理</returns>
    /// <remarks>
    /// 1. 审计自动化：自动为所有继承 BaseEntity 的实体设置创建时间和更新时间，减少手动维护成本
    /// 2. 时间统一：使用 UTC 时间确保跨时区系统的时间一致性，避免时区转换问题
    /// 3. 状态感知：根据实体状态（新增、修改）执行不同的审计字段处理逻辑
    /// 4. 性能考虑：在保存变更前批量处理所有实体的审计字段，减少数据库往返次数
    /// 5. 多租户安全：确保TenantId正确赋值，防止越权操作其他租户数据
    /// </remarks>
    public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
    {
      // 新增：多租户安全校验（先执行，确保TenantId正确后再处理审计字段）
      ValidateMultiTenancyData();

      // 保留您原始的审计字段处理逻辑（未做任何修改）
      var entries = ChangeTracker.Entries<BaseEntity>();
      foreach (var entry in entries)
      {
        if (entry.State == EntityState.Added)
        {
          entry.Entity.CreatedTime = DateTime.UtcNow;
          entry.Entity.UpdatedTime = DateTime.UtcNow;

          // 您原始的逻辑：确保创建人字段不为空，如果未设置则使用系统默认值
          if (string.IsNullOrEmpty(entry.Entity.CreatedBy))
          {
            entry.Entity.CreatedBy = "system";
          }
        }
        else if (entry.State == EntityState.Modified)
        {
          entry.Entity.UpdatedTime = DateTime.UtcNow;

          // 您原始的可选逻辑：禁止修改创建时间（防止误操作篡改审计信息）
          entry.Property(e => e.CreatedTime).IsModified = false;
          // 补充：建议同时禁止修改创建人（保持审计信息完整性）
          entry.Property(e => e.CreatedBy).IsModified = false;
        }

        // 您原始的备注：软删除实体的处理（由具体业务逻辑或仓储层处理）
      }

      // 调用基类方法完成实际的数据保存操作
      return await base.SaveChangesAsync(cancellationToken);
    }

    /// <summary>
    /// 重写同步保存方法，确保同步操作也具备审计字段自动维护和多租户安全校验能力
    /// </summary>
    /// <returns>受影响的数据记录行数</returns>
    /// <remarks>
    /// 1. 一致性保障：确保同步和异步保存操作的审计行为、多租户校验逻辑一致
    /// 2. 异步优先：在现代应用中推荐使用异步方法，此方法主要用于兼容性场景
    /// 3. 异常处理：同步方法中的异常会直接抛出，调用方需要适当处理
    /// </remarks>
    public override int SaveChanges()
    {
      // 新增：多租户安全校验
      ValidateMultiTenancyData();

      // 保留您原始的审计字段处理逻辑（未做任何修改）
      var entries = ChangeTracker.Entries<BaseEntity>();
      foreach (var entry in entries)
      {
        if (entry.State == EntityState.Added)
        {
          entry.Entity.CreatedTime = DateTime.UtcNow;
          entry.Entity.UpdatedTime = DateTime.UtcNow;

          if (string.IsNullOrEmpty(entry.Entity.CreatedBy))
          {
            entry.Entity.CreatedBy = "system";
          }
        }
        else if (entry.State == EntityState.Modified)
        {
          entry.Entity.UpdatedTime = DateTime.UtcNow;
          entry.Property(e => e.CreatedTime).IsModified = false;
          entry.Property(e => e.CreatedBy).IsModified = false;
        }
      }

      return base.SaveChanges();
    }

    /// <summary>
    /// 多租户数据安全校验（核心新增方法）
    /// 1. INSERT：自动为ITenantAware实体设置TenantId，防止数据归属错误
    /// 2. UPDATE/DELETE：验证实体租户归属，防止越权操作其他租户数据
    /// </summary>
    private void ValidateMultiTenancyData()
    {
      var currentTenantId = TenantContextAccessor.TenantId;
      var isSystemAdmin = TenantContextAccessor.IsSystemAdmin;

      #region 1. 处理INSERT操作：自动设置TenantId
      foreach (var entry in ChangeTracker.Entries<ITenantAware>())
      {
        if (entry.State == EntityState.Added)
        {
          // 校验：非系统管理员必须有租户上下文（防止无租户ID的数据创建）
          if (string.IsNullOrEmpty(currentTenantId) && !isSystemAdmin)
          {
            throw new InvalidOperationException("无法创建多租户实体：当前没有有效的租户上下文（非系统管理员）。");
          }

          // 自动填充TenantId（如果开发者未手动设置）
          if (string.IsNullOrEmpty(entry.Entity.TenantId))
          {
            entry.Entity.TenantId = currentTenantId; // 系统管理员时为null（全局数据）
          }
        }
      }
      #endregion

      #region 2. 处理UPDATE/DELETE操作：验证租户权限（系统管理员跳过校验）
      if (isSystemAdmin)
      {
        return; // 系统管理员可操作所有租户数据，无需校验
      }

      foreach (var entry in ChangeTracker.Entries<ITenantAware>())
      {
        if (entry.State == EntityState.Modified || entry.State == EntityState.Deleted)
        {
          var entityTenantId = entry.Entity.TenantId;

          // 校验1：实体必须有租户ID（排除全局数据）
          if (string.IsNullOrEmpty(entityTenantId))
          {
            var entityType = entry.Entity.GetType().Name;
            var entityId = GetEntityPrimaryKey(entry);
            throw new SecurityException($"操作拒绝：无法修改/删除全局数据（{entityType} ID: {entityId}），仅系统管理员有权限。");
          }

          // 校验2：实体租户ID必须与当前租户一致
          if (entityTenantId != currentTenantId)
          {
            var entityType = entry.Entity.GetType().Name;
            var entityId = GetEntityPrimaryKey(entry);
            throw new SecurityException($"操作拒绝：当前租户（{currentTenantId}）无权修改/删除其他租户的{entityType}数据（ID: {entityId}，所属租户：{entityTenantId}）。");
          }
        }
      }
      #endregion
    }

    /// <summary>
    /// 获取实体的主键值（用于异常信息中的精准定位）
    /// </summary>
    /// <param name="entry">实体变更跟踪项</param>
    /// <returns>主键值字符串（多主键时用逗号分隔）</returns>
    private string GetEntityPrimaryKey(EntityEntry entry)
    {
      var primaryKeyProperties = entry.Metadata.FindPrimaryKey()?.Properties;
      if (primaryKeyProperties == null || !primaryKeyProperties.Any())
      {
        return "未知";
      }

      var keyValues = primaryKeyProperties
          .Select(p => entry.Property(p.Name).CurrentValue?.ToString() ?? "null")
          .ToArray();

      return string.Join(", ", keyValues);
    }
  }
}